package com.hmdp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.hmdp.dto.Result;
import com.hmdp.entity.ShopType;
import com.hmdp.mapper.ShopTypeMapper;
import com.hmdp.service.IShopTypeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.hmdp.utils.RedisCommontCreateUtils.setJsonValue;
import static com.hmdp.utils.RedisConstants.SHOP_TYPE_CACHE_KEY;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22<br>
 * 判断是否有缓存,如果有缓存,则直接返回缓存数据,如果没有缓存,则判断缓存是否过期<br>
 * 如果缓存过期,则尝试获取互斥锁,如果获取成功,则开启一条独立线程,用来查询数据库获取最新商铺数据,缓存到redis中,并释放锁<br>
 * 原本的线程则直接返回过期的旧缓存<br>
 * 如果获取互斥锁失败,则返回过期的旧缓存<br>
 * 开启的线程重建完缓存后,用户再次访问时,会获取到新缓存<br>
 */
@Service
@Slf4j
public class ShopTypeServiceImpl extends ServiceImpl<ShopTypeMapper, ShopType> implements IShopTypeService {
    /**
     * 查询店铺类型列表
     *
     * @return
     */
    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result queryShopTypeList() {
        String key = SHOP_TYPE_CACHE_KEY;
        //1.先从redis中查询缓存
        String shopTypeList = (String) redisTemplate.opsForValue().get(key);//获取hash类型的缓存
        //判断是否有缓存
        if (StrUtil.isNotBlank(shopTypeList) && StrUtil.isNotEmpty(shopTypeList)) {//如果缓存不为空并且不为空字符串,直接返回缓存数据
            //JSON转换成List<ShopType>
            List<ShopType> typeList = JSONUtil.toList(shopTypeList, ShopType.class);
            log.error("从redis中获取店铺类型列表:{}", shopTypeList);
            return Result.ok(typeList);
        }
        //1--如果为空对象(空值),则返回错误信息,为null才会查询数据库
        if (shopTypeList != null) {
            //为null,则查询数据库,为空值则返回错误信息
            log.error("此商铺数据为空,缓存中的值为空值");
            return Result.fail("此店铺数据不存在");
        }
        //2.如果redis中没有缓存,查询数据库,并将结果缓存到redis中
        //业务实现:查询店铺类型列表
        List<ShopType> typeList = query().orderByAsc("sort").list();// 根据排序字段sort进行排序,查询所有店铺类型
        //判断是否有数据
        if (Objects.isNull(typeList) || typeList.isEmpty()) {//如果没有数据,返回错误信息
            /*缓存空对象*/
            setJsonValue(key, "", Duration.ofMinutes(5), stringRedisTemplate);
            //redisTemplate.opsForValue().set(key, "", 60L, TimeUnit.MINUTES);//缓存空对象,60分钟过期
            log.error("没有查询到店铺类型数据");
            return Result.fail("没有查询到店铺类型数据");
        }
        //3.数据库中有数据,将查询结果缓存到redis中,返回查询结果
        /*redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(typeList), 60L, TimeUnit.MINUTES);*/
        setJsonValue(key, JSONUtil.toJsonStr(typeList), Duration.ofMinutes(60), stringRedisTemplate);
        //4.返回查询结果
        return Result.ok(typeList);
    }
}

